package com.xu.cloud.common.process.support;

import com.xu.cloud.common.pojo.http.req.BaseRequest;
import com.xu.cloud.common.pojo.http.resp.BaseResponse;
import com.xu.cloud.common.process.IProcessSelector;
import com.xu.cloud.common.process.IProcessor;
import org.springframework.aop.support.AopUtils;
import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;

import java.lang.reflect.Method;
import java.lang.reflect.ParameterizedType;
import java.util.Map;

/**
 * 根据入参和出参类型匹配选择器
 *
 * @author xuguan
 * @since 2025/1/17
 */
@SuppressWarnings({"rawtypes", "NullableProblems"})
public class BestMatchedProcessSelector implements IProcessSelector, ApplicationContextAware {

    private ApplicationContext applicationContext;

    @Override
    public IProcessor select(Method method) {
        final int parameterCount = method.getParameterCount();
        if (parameterCount != 1) {
            return new NopProcessor();
        }
        final Class<?> parameterType = method.getParameterTypes()[0];
        if (!BaseRequest.class.isAssignableFrom(parameterType)) {
            return new NopProcessor();
        }
        final Class<?> returnType = method.getReturnType();
        if (!(BaseResponse.class.isAssignableFrom(returnType))) {
            return new NopProcessor();
        }

        final Map<String, IProcessor> processorMap = applicationContext.getBeansOfType(IProcessor.class);
        for (Map.Entry<String, IProcessor> processorEntry : processorMap.entrySet()) {
            final IProcessor processor = processorEntry.getValue();
            final Class<?> targetClass = AopUtils.getTargetClass(processor);
            final ParameterizedType parameterizedType = (ParameterizedType) targetClass.getGenericSuperclass();

            if (parameterizedType.getActualTypeArguments().length == 2
                    && parameterizedType.getActualTypeArguments()[0] == parameterType
                    && parameterizedType.getActualTypeArguments()[1] == returnType) {
                return processor;
            }
        }

        return new NopProcessor();
    }

    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        this.applicationContext = applicationContext;
    }
}
